﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Petra.Gis.DataServices.WKT
{

  /// <summary>
  /// regex geometry is universal frame where any WKT geometry graph can fit into.
  /// each geometry element(figure,shape) in graph is represented as node in hierarchy tree
  /// and each node keeps start and end position in original string
  /// </summary>
  public class RegexGeometry : IEnumerable<RegexGeometry>
  {

    public enum NodeTypeEnum
    {
      Undefined = 0,
      Collection = 2,
      Coordlist = 3
    }

    //private int _startIndex;
    //private int _endIndex;
    //private string _groupName;
    //private NodeType _nodeType;
    private List<RegexGeometry> _items;
    private RegexGeometry _parent;
    //private RegexGeometryParser _rgp;


    //root node constructor
    internal RegexGeometry(RegexGeometryParser rgp)
    {
      //root node constructor
      //_nodeType = NodeType.Root;
      Parser = rgp;
      GroupName = "geometry";
      StartIndex = 0;
      EndIndex = 1;
      init(null, 1);
    }



    //child node constructor
    internal RegexGeometry(RegexGeometry parent, string groupName, int currentCollectionLevel, int startIndex, int endIndex)
    {
      //_nodeType = NodeType.Undefined;
      GroupName = groupName;
      StartIndex = startIndex;
      EndIndex = endIndex;
      Parser = parent.Parser;
      init(parent, currentCollectionLevel);
    }




    private void init(RegexGeometry parent, int currentCollectionLevel)
    {
      _parent = parent;

      int noOfItems = getTotalNumOfGeometries(currentCollectionLevel);

      if (noOfItems == 0) //collection not found, treat as coordlist
        prepareCoordList();

      else
        prepareCollection(currentCollectionLevel);

    }




    /// <summary>
    /// get index of geometry element start
    /// within global regex CaptureCollection.
    /// </summary>
    public int StartIndex
    { get; private set; }




    /// <summary>
    /// get index of geometry element end 
    /// within global regex CaptureCollection.
    /// </summary>
    public int EndIndex
    { get; private set; }



    public string GroupName
    { get; private set; }



    public NodeTypeEnum NodeType
    { get; private set; }



    public RegexGeometryParser Parser
    { get; private set; }



    private void prepareCoordList()
    {

      if (_parent == null)
      {
        //root node is coordlist, simple geom
        //StartIndex = Parser.GetStartByCapture("coordlist", Parser.RegexMatch.Groups["geometry"].Captures[0].Index);
        //EndIndex = Parser.GetEndByCapture("coordlist", Parser.RegexMatch.Groups["geometry"].Captures[1].Index);
        StartIndex = Parser.GetStartByCapture("coordlist", 0);
        EndIndex = Parser.GetEndByCapture("coordlist", Parser.RegexMatch.Groups["coordlist"].Captures[Parser.RegexMatch.Groups["coordlist"].Captures.Count - 1].Index);
      }
      else
      {
        StartIndex = Parser.GetStartByCapture("coordlist", Parser.RegexMatch.Groups[GroupName].Captures[StartIndex].Index);
        EndIndex = Parser.GetEndByCapture("coordlist", Parser.RegexMatch.Groups[GroupName].Captures[EndIndex].Index);
      }

      //after geometry position setup, turn this geometry group into coordlist
      this.NodeType = NodeTypeEnum.Coordlist;
      this.GroupName = "coordlist";

    }




    private void prepareCollection(int currentCollectionLevel)
    {
      //if (_groupName == null)
      //  _groupName = "geometry" + currentCollectionLevel.ToString();

      NodeType = NodeTypeEnum.Collection;

      RegexGeometry tmpRg;
      int startPos;
      int endPos;

      //setup position for current node
      if (_parent == null)
      {
        //root node is coordlist
        startPos = Parser.GetStartByCapture("geometry" + currentCollectionLevel.ToString(), Parser.RegexMatch.Groups["geometry"].Captures[0].Index);
        endPos = Parser.GetEndByCapture("geometry" + currentCollectionLevel.ToString(), Parser.RegexMatch.Groups["geometry"].Captures[1].Index);
      }
      else
      {
        startPos = Parser.GetStartByCapture("geometry" + currentCollectionLevel.ToString(), Parser.RegexMatch.Groups[GroupName].Captures[StartIndex].Index);
        endPos = Parser.GetEndByCapture("geometry" + currentCollectionLevel.ToString(), Parser.RegexMatch.Groups[GroupName].Captures[EndIndex].Index);
      }

      //validate if start and end gives pair
      //if ((endPos - startPos + 1) % 2 != 0)
      //  throw new ArgumentException("invalid wkt representation","wktstring");

      //create subnodes...
      _items = new List<RegexGeometry>();
      while (startPos < endPos)
      {
        tmpRg = new RegexGeometry(this, "geometry" + currentCollectionLevel.ToString(), currentCollectionLevel + 1, startPos, startPos + 1);
        _items.Add(tmpRg);
        startPos += 2; //regex tokens are always setup as pair
      }
    }




    //IEnumerator
    //public IEnumerator<T> GetEnumerator()
    public IEnumerator<RegexGeometry> GetEnumerator()
    {
      //while (_items.)
      foreach (RegexGeometry rg in _items)
      {
        yield return rg;
      }
    }




    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }




    public RegexGeometry this[int index]
    {
      get { return _items[index]; }
    }




    private int getTotalNumOfGeometries(int currentCollectionLevel)
    {
      int noOfItems;
      //test for collections...
      try
      {
        CaptureCollection cc = Parser.RegexMatch.Groups["geometry" + currentCollectionLevel.ToString()].Captures;
        noOfItems = cc.Count;
      }
      catch { noOfItems = 0; }

      return noOfItems;
    }

  }

}
